import {
  HTTP_STATUS_CODES,
  XHR_EVENTS,
  XHR_REQUEST_PROPERTIES,
  XHR_RESPONSE_PROPERTIES,
  XHR_STATES,
} from "./constant";
import { createSingle } from "./utils";

const createXHRHook = ({ onRequest, onResponse }: CreateHookProps) => {
  const NativeXMLHttpRequest = window.XMLHttpRequest;
  class MockXMLHttpRequest {
    private _send: any;
    onload: (this: XMLHttpRequest, ev: ProgressEvent<EventTarget>) => any;
    constructor() {
      this.ctx.setHeader = (key, value) => {
        this.ctx.headers[key] = value;
      };
    }
    _events: Record<string, any> = {};
    addEventListener(type, handle) {
      var events = this._events;
      if (!events[type]) events[type] = [];
      events[type].push(handle);
    }
    dispatchEvent(event) {
      var handles = this._events[event.type] || [];
      for (var i = 0; i < handles.length; i++) {
        handles[i].call(this, event);
      }
    
      var ontype = "on" + event.type;
      if (this[ontype]) this[ontype](event);
    }
    xhr: XMLHttpRequest;
    //  static   readonly UNSENT: 0;
    //  static   readonly OPENED: 1;
    //  static   readonly HEADERS_RECEIVED: 2;
    //  static    readonly LOADING: 3;f
    //  static    readonly DONE: 4;
    onreadystatechange: ((this: Document, ev: Event) => any) | null;
    readyState: number;
    response: string;
    responseText: string;
    responseURL: number;
    status: number;
    statusText: string;
    ctx: ContextProps = {
      url: "",
      resBody: null,
      data: null,
      method: "",
      resource: "xhr",
      delay: null,
      headers: {},
      setHeader: function (key: string, value: string): void {
        throw new Error("Function not implemented.");
      },
    };
    _sendMockBody(resBody) {
      this.ctx.resBody = resBody;
      this.readyState = XMLHttpRequest.HEADERS_RECEIVED;
      this.dispatchEvent(new Event("readystatechange"));
      this.readyState = XMLHttpRequest.LOADING;
      this.dispatchEvent(new Event("readystatechange"));
      this.status = 200;
      this.readyState = XMLHttpRequest.DONE;

      this.statusText = HTTP_STATUS_CODES[200];
      this.dispatchEvent(new Event("readystatechange"));
      this.response = resBody;
      this.responseText = resBody;

      setTimeout(
        () => this.dispatchEvent(new Event("readystatechange")),
        this.ctx.delay || 0
      );
    }
    open(method: string, url: string | URL,async?:boolean) {
      const that = this;
      this.ctx.method = method;
      this.ctx.url = url;
      this.ctx.async = async;
      onRequest(this.ctx, {
        next(ctx) {
          that._send = true;
        },
        send(resBody) {
          that._sendMockBody(resBody);
        },
      });
    }

    abort() {
      if (this.xhr) {
        this.xhr.abort();
      }
    }
    setRequestHeader(name,value){
       this.ctx.setHeader(name,value)
    }
    getAllResponseHeaders(){
      return  this.xhr.getAllResponseHeaders()
    }
    getResponseHeader(name:string){
      if(this.xhr){
        return this.xhr.getResponseHeader[name.toLowerCase()]
      }
      // return this.ctx.responseHeaders[name.toLowerCase()]

    }
    send(data: Document | XMLHttpRequestBodyInit) {
      if (!this._send) {
        return;
      }
      this.ctx.data = data;
      this.xhr = new NativeXMLHttpRequest();
      this.xhr.open(this.ctx.method, this.ctx.url,this.ctx.async);
      for (var j = 0; j < XHR_REQUEST_PROPERTIES.length; j++) {
        try {
          this.xhr[XHR_REQUEST_PROPERTIES[j]] = this[XHR_REQUEST_PROPERTIES[j]];
        } catch (e) {}
      }
    
      Object.keys(this.ctx.headers).forEach((key) => {
        this.xhr.setRequestHeader(key, this.ctx.headers[key]);
      });
      this.xhr.send(data);
      function handle(event) {
        // 同步属性 NativeXMLHttpRequest => MockXMLHttpRequest
        for (var i = 0; i < XHR_RESPONSE_PROPERTIES.length; i++) {
          try {
            that[XHR_RESPONSE_PROPERTIES[i]] =
              that.xhr[XHR_RESPONSE_PROPERTIES[i]];
          } catch (e) {}
        }
        // 触发 MockXMLHttpRequest 上的同名事件
        that.dispatchEvent(new Event(event.type /*, false, false, that*/));
      }
      // 初始化所有事件，用于监听原生 XHR 对象的事件
      for (var i = 0; i < XHR_EVENTS.length; i++) {
        this.addEventListener(XHR_EVENTS[i], handle);
      }
      const that = this;
      
      this.xhr.onload = that.onload
      this.xhr.addEventListener("readystatechange", function () {
        that.readyState = this.readyState;
        that.status = this.status;
        that.statusText = HTTP_STATUS_CODES[this.status];
        if (that.readyState === 4) {
          that.ctx.resBody = this.responseText;

          onResponse(that.ctx, {
            send(resBody) {
           

              that.ctx.resBody = resBody;
              that.response = resBody;
              // that.responseJSON= {ss:"sss"};
              that.responseText = resBody;
              setTimeout(
                () => that.dispatchEvent(new Event("readystatechange")),
                that.ctx.delay || 0
              );
            },
            next(ctx) {
              that.response = this.response;
              that.response = ctx.resBody;
              that.responseText = ctx.resBody;
              setTimeout(
                () => that.dispatchEvent(new Event("readystatechange")),
                ctx.delay || 0
              );
            },
          });
        } else {
          that.dispatchEvent(new Event("readystatechange"));
        }

        // console.log('返回内容',this.response,this.readyState)
      });
    }
  }
  Object.keys(XHR_STATES).forEach((key) => {
    MockXMLHttpRequest[key] = XHR_STATES[key];
  });
  window.XMLHttpRequest = MockXMLHttpRequest as any;
};
export default createXHRHook;
